package com.sudoku.board;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import com.badlogic.gdx.math.Vector2;
import com.sudoku.observer.Subject;

/**
 * Ordinary Sudoku.
 */
public class CorrectBoard extends Board {
	private Subject subject;

	public CorrectBoard(Subject subject) {
		super(subject);
		this.subject = subject;
		data = new ArrayList<Box>();
		for (int i = 0; i < 3; i++) {
			for (int j = 2; j >= 0; j--) {
				Box b = new Box(new Vector2(i, j), true, subject);
				data.add(b);
			}
		}
	}

	/*
	 * Solve first row of boxes
	 */
	public int solveFirstRow() {
		int mistakes = 0;
		mistakes += solveUpperMiddle();
		if (mistakes == 0) {
			solveUpperRight();
		}
		return mistakes;
	}

	/*
	 * Solve first column of boxes
	 */
	public int solveFirstColumn() {
		int mistakes = 0;
		mistakes += solveMiddleLeft();
		if (mistakes == 0) {
			solveLowerLeft();
		}
		return mistakes;
	}

	public int solveMiddleRow() {
		int mistakes = 0;
		mistakes += solveMiddleMiddle();
		return mistakes;
	}

	// completely random
	public int solveUpperLeft() {
		int mistakes = 0;
		data.set(POSITION.UPPER_LEFT.ordinal(), data.get(POSITION.UPPER_LEFT.ordinal()).shuffle());
		return mistakes;
	}

	// choose random values until it works
	public int solveUpperMiddle() {
		int mistakes = 0;
		for (int i = 0; i < 3; i++) {
			Set<Integer> duplicates = new HashSet<Integer>();
			// iterate over first 6 tiles in a row (3 for upper left box + 3 for
			// upper middle box)
			for (int col = 0; col < 6; col++) {
				if (duplicates.contains(this.getBoardRow(i).get(col))) {
					mistakes++;
				} else {
					duplicates.add(this.getBoardRow(i).get(col));
				}
			}
		}
		// if there were duplicate values, shuffle box again
		if (mistakes != 0) {
			data.set(POSITION.UPPER_MIDDLE.ordinal(), data.get(POSITION.UPPER_MIDDLE.ordinal()).shuffle());
		}
		return mistakes;
	}

	// fill in this box with whatever is left over from the boxes left of it
	public void solveUpperRight() {
		Box b = new Box(data.get(POSITION.UPPER_RIGHT.ordinal()).getCoords(), true, subject);
		// go through all rows in box
		for (int i = 0; i < 3; i++) {
			ArrayList<Integer> firstSixValues = new ArrayList<Integer>();
			ArrayList<Integer> values = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
			for (int j = 0; j < 2; j++) {
				firstSixValues.addAll(data.get(j * 3).getBoxRow(i));
			}
			values.removeAll(firstSixValues);
			for (int rand = 0; rand < 3; rand++) {
				int r = (int) (Math.random() * values.size());
				b.getData().get(rand * 3 + i).setData(Integer.toString(values.get(r)).charAt(0));
				values.remove(r);
			}
		}
		data.set(POSITION.UPPER_RIGHT.ordinal(), b);
	}

	// choose random values until it works
	public int solveMiddleLeft() {
		int mistakes = 0;
		for (int i = 0; i < 3; i++) {
			Set<Integer> duplicates = new HashSet<Integer>();
			// iterate over first 6 tiles in a col (3 for upper left box + 3 for
			// middle left box)
			for (int row = 0; row < 6; row++) {
				if (duplicates.contains(this.getBoardCol(i).get(row))) {
					mistakes++;
				} else {
					duplicates.add(this.getBoardCol(i).get(row));
				}
			}
		}
		if (mistakes != 0) {
			data.set(POSITION.MIDDLE_LEFT.ordinal(), data.get(POSITION.MIDDLE_LEFT.ordinal()).shuffle());
		}
		return mistakes;
	}

	// fill in this box with whatever is left over from the boxes above it
	public void solveLowerLeft() {
		Box b = new Box(data.get(POSITION.BOTTOM_LEFT.ordinal()).getCoords(), true, subject);
		// go through all rows in box
		for (int i = 0; i < 3; i++) {
			ArrayList<Integer> firstSixValues = new ArrayList<Integer>();
			ArrayList<Integer> values = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
			for (int j = 0; j < 2; j++) {
				firstSixValues.addAll(data.get(j).getBoxCol(i));
			}
			values.removeAll(firstSixValues);
			for (int rand = 0; rand < 3; rand++) {
				int r = (int) (Math.random() * values.size());
				b.getData().get(rand + i * 3).setData(Integer.toString(values.get(r)).charAt(0));
				values.remove(r);
			}
		}
		data.set(POSITION.BOTTOM_LEFT.ordinal(), b);
	}

	public int solveMiddleMiddle() {
		int mistakes = 0;
		ArrayList<Integer> setOfValues = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
		ArrayList<Integer> values = new ArrayList<Integer>();
		Box b = new Box(data.get(POSITION.MIDDLE_MIDDLE.ordinal()).getCoords(), true, subject);
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				values.addAll(setOfValues);
				values.removeAll(data.get(1).getBoxRow(j));
				values.removeAll(data.get(3).getBoxCol(i));
				int r = (int) (Math.random() * values.size());
				if (values.size() == 0) {
					// found a mistake, need to redo
					return 1;
				} else {
					b.getData().get(i * 3 + j).setData(Integer.toString(values.get(r)).charAt(0));
					setOfValues.remove(values.get(r));
					values.clear();
				}
			}
		}
		data.set(POSITION.MIDDLE_MIDDLE.ordinal(), b);
		return mistakes;
	}

	public int solveLowerMiddle() {
		int mistakes = 0;
		ArrayList<Integer> setOfValues = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
		ArrayList<Integer> values = new ArrayList<Integer>();
		Box b = new Box(data.get(POSITION.BOTTOM_MIDDLE.ordinal()).getCoords(), true, subject);
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				values.addAll(setOfValues);
				values.removeAll(data.get(2).getBoxRow(j));
				values.removeAll(data.get(3).getBoxCol(i));
				values.removeAll(data.get(4).getBoxCol(i));
				int r = (int) (Math.random() * values.size());
				if (values.size() == 0) {
					// found a mistake, need to redo
					return 1;
				} else {
					b.getData().get(i * 3 + j).setData(Integer.toString(values.get(r)).charAt(0));
					setOfValues.remove(values.get(r));
					values.clear();
				}
			}
		}
		data.set(POSITION.BOTTOM_MIDDLE.ordinal(), b);
		return mistakes;
	}

	public int solveMiddleRight() {
		int mistakes = 0;
		ArrayList<Integer> setOfValues = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
		ArrayList<Integer> values = new ArrayList<Integer>();
		Box b = new Box(data.get(POSITION.MIDDLE_RIGHT.ordinal()).getCoords(), true, subject);
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				values.addAll(setOfValues);
				values.removeAll(data.get(1).getBoxRow(j));
				values.removeAll(data.get(4).getBoxRow(j));
				values.removeAll(data.get(6).getBoxCol(i));
				int r = (int) (Math.random() * values.size());
				if (values.size() == 0) {
					// found a mistake, need to redo
					return 1;
				} else {
					b.getData().get(i * 3 + j).setData(Integer.toString(values.get(r)).charAt(0));
					setOfValues.remove(values.get(r));
					values.clear();
				}
			}
		}
		data.set(POSITION.MIDDLE_RIGHT.ordinal(), b);
		return mistakes;
	}

	// last tile to solve
	public int solveBottomRight() {
		int mistakes = 0;
		ArrayList<Integer> setOfValues = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
		ArrayList<Integer> values = new ArrayList<Integer>();
		Box b = new Box(data.get(POSITION.BOTTOM_RIGHT.ordinal()).getCoords(), true, subject);
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				values.addAll(setOfValues);
				values.removeAll(data.get(2).getBoxRow(j));
				values.removeAll(data.get(5).getBoxRow(j));
				values.removeAll(data.get(6).getBoxCol(i));
				values.removeAll(data.get(7).getBoxCol(i));
				int r = (int) (Math.random() * values.size());
				if (values.size() == 0) {
					// found a mistake, need to redo
					return 1;
				} else {
					b.getData().get(i * 3 + j).setData(Integer.toString(values.get(r)).charAt(0));
					setOfValues.remove(values.get(r));
					values.clear();
				}
			}
		}
		data.set(POSITION.BOTTOM_RIGHT.ordinal(), b);
		return mistakes;
	}

}
